/*-------------------------------------------------------------------------
 *
 * xact.h
 *	  postgres 事务系统定义
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/xact.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef XACT_H
#define XACT_H

#include "access/transam.h"
#include "access/xlogreader.h"
#include "datatype/timestamp.h"
#include "lib/stringinfo.h"
#include "nodes/pg_list.h"
#include "storage/relfilenode.h"
#include "storage/sinval.h"

/*
 * 全局事务 ID 的最大大小（包括 '\0'）。
 *
 * 请注意，GIDSIZE 的最大值必须适合 uint16 gidlen，
 * 在 TwoPhaseFileHeader 中指定。
 */
#define GIDSIZE 200

/*
 * 事务隔离级别
 */
#define XACT_READ_UNCOMMITTED	0
#define XACT_READ_COMMITTED		1
#define XACT_REPEATABLE_READ	2
#define XACT_SERIALIZABLE		3

extern PGDLLIMPORT int DefaultXactIsoLevel;
extern PGDLLIMPORT int XactIsoLevel;

/*
 * 我们在内部实现了三个隔离级别。
 * 两个更强的级别为每个数据库事务使用一个快照；
 * 其他级别为每个语句使用一个快照。
 * 可串行化使用谓词锁以及快照。
 * 这些宏应该用于检查选择了哪个隔离级别。
 */
#define IsolationUsesXactSnapshot() (XactIsoLevel >= XACT_REPEATABLE_READ)
#define IsolationIsSerializable() (XactIsoLevel == XACT_SERIALIZABLE)

/* 事务只读状态 */
extern PGDLLIMPORT bool DefaultXactReadOnly;
extern PGDLLIMPORT bool XactReadOnly;

/* 该事务中记录语句的标志 */
extern PGDLLIMPORT bool xact_is_sampled;

/*
 * 事务是可延迟的——目前仅对只读
 * 可串行化事务有意义
 */
extern PGDLLIMPORT bool DefaultXactDeferrable;
extern PGDLLIMPORT bool XactDeferrable;

typedef enum
{
	SYNCHRONOUS_COMMIT_OFF,		/* 异步提交 */
	SYNCHRONOUS_COMMIT_LOCAL_FLUSH, /* 仅等待本地刷新 */
	SYNCHRONOUS_COMMIT_REMOTE_WRITE,	/* 等待本地刷新和远程
										 * 写入 */
	SYNCHRONOUS_COMMIT_REMOTE_FLUSH,	/* 等待本地和远程刷新 */
	SYNCHRONOUS_COMMIT_REMOTE_APPLY /* 等待本地和远程刷新以及
									 * 远程应用 */
}			SyncCommitLevel;

/* 定义同步提交的默认设置 */
#define SYNCHRONOUS_COMMIT_ON	SYNCHRONOUS_COMMIT_REMOTE_FLUSH

/* 同步提交级别 */
extern PGDLLIMPORT int synchronous_commit;

#ifdef FDDQ
/* 开启/关闭逻辑复制 */
extern PGDLLIMPORT bool	enable_logical_replication;
#endif

/* 在事务的逻辑流中使用 */
extern PGDLLIMPORT TransactionId CheckXidAlive;
extern PGDLLIMPORT bool bsysscan;

/*
 * 杂项标志位，用于记录在顶层
 * 事务中发生的事件。这些标志仅保存在 MyXactFlags 中，
 * 目的是让我们记得在事务中稍后做某些事情。这是
 * 全局可访问的，因此可以从代码的任何需要记录标志的地方设置。
 */
extern PGDLLIMPORT int MyXactFlags;

/*
 * XACT_FLAGS_ACCESSEDTEMPNAMESPACE - 一旦访问临时对象即设置。
 * 在这种情况下，我们不允许执行PREPARE TRANSACTION。
 */
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE		(1U << 0)

/*
 * XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK - 记录顶层xact是否
 * 记录了任何独占访问锁。
 */
#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK	(1U << 1)

/*
 * XACT_FLAGS_NEEDIMMEDIATECOMMIT - 记录顶层语句
 * 是否需要立即提交，例如CREATE DATABASE。
 */
#define XACT_FLAGS_NEEDIMMEDIATECOMMIT			(1U << 2)

/*
 * XACT_FLAGS_PIPELINING - 在我们完成扩展查询协议
 * Execute 消息时设置。这对于检测通过流水线创建的
 * 隐式事务块是有用的。
 */
#define XACT_FLAGS_PIPELINING					(1U << 3)

/*
 *	为动态加载模块提供的事务开始和结束回调
 */
typedef enum
{
	XACT_EVENT_COMMIT,
	XACT_EVENT_PARALLEL_COMMIT,
	XACT_EVENT_ABORT,
	XACT_EVENT_PARALLEL_ABORT,
	XACT_EVENT_PREPARE,
	XACT_EVENT_PRE_COMMIT,
	XACT_EVENT_PARALLEL_PRE_COMMIT,
	XACT_EVENT_PRE_PREPARE
} XactEvent;

typedef void (*XactCallback) (XactEvent event, void *arg);

typedef enum
{
	SUBXACT_EVENT_START_SUB,
	SUBXACT_EVENT_COMMIT_SUB,
	SUBXACT_EVENT_ABORT_SUB,
	SUBXACT_EVENT_PRE_COMMIT_SUB
} SubXactEvent;

typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
								 SubTransactionId parentSubid, void *arg);

/* 保存/恢复事务特征的数据结构 */
typedef struct SavedTransactionCharacteristics
{
	int			save_XactIsoLevel;
	bool		save_XactReadOnly;
	bool		save_XactDeferrable;
} SavedTransactionCharacteristics;


/* ----------------
 *		事务相关的XLOG条目
 * ----------------
 */

/*
 * XLOG允许在日志记录xl_info字段的高4位存储一些信息。
 * 我们使用3位用于操作码，1位用于可选标志变量。
 */
#define XLOG_XACT_COMMIT			0x00
#define XLOG_XACT_PREPARE			0x10
#define XLOG_XACT_ABORT				0x20
#define XLOG_XACT_COMMIT_PREPARED	0x30
#define XLOG_XACT_ABORT_PREPARED	0x40
#define XLOG_XACT_ASSIGNMENT		0x50
#define XLOG_XACT_INVALIDATIONS		0x60
/* 空闲操作码0x70 */

/* 用于过滤xl_info中的操作码的掩码 */
#define XLOG_XACT_OPMASK			0x70

/* 此记录是否有'xinfo'字段 */
#define XLOG_XACT_HAS_INFO			0x80

/*
 * 以下标志存储在xinfo中，确定提交/中止记录
 * 包含哪些信息。
 */
#define XACT_XINFO_HAS_DBINFO			(1U << 0)
#define XACT_XINFO_HAS_SUBXACTS			(1U << 1)
#define XACT_XINFO_HAS_RELFILENODES		(1U << 2)
#define XACT_XINFO_HAS_INVALS			(1U << 3)
#define XACT_XINFO_HAS_TWOPHASE			(1U << 4)
#define XACT_XINFO_HAS_ORIGIN			(1U << 5)
#define XACT_XINFO_HAS_AE_LOCKS			(1U << 6)
#define XACT_XINFO_HAS_GID				(1U << 7)
#define XACT_XINFO_HAS_DROPPED_STATS	(1U << 8)

#ifdef FDDQ
/*
* 这表明此事务影响的数据不需要包含在
* 逻辑复制中。
*/
#define XACT_XINFO_LOGIREPL_OFF			(1U << 22)
#endif
/*
 * 同样存储在xinfo中，这些指示在恢复期间
 * 模拟事务效果时需要发生的各种额外操作。
 *
 * 它们被命名为XactCompletion...以将它们与
 * 在原始事务完成结束时运行的EOXact...例程区分开来。
 */
#define XACT_COMPLETION_APPLY_FEEDBACK			(1U << 29)
#define XACT_COMPLETION_UPDATE_RELCACHE_FILE	(1U << 30)
#define XACT_COMPLETION_FORCE_SYNC_COMMIT		(1U << 31)

/* 上述标志的访问宏 */
#define XactCompletionApplyFeedback(xinfo) \
	((xinfo & XACT_COMPLETION_APPLY_FEEDBACK) != 0)
#define XactCompletionRelcacheInitFileInval(xinfo) \
	((xinfo & XACT_COMPLETION_UPDATE_RELCACHE_FILE) != 0)
#define XactCompletionForceSyncCommit(xinfo) \
	((xinfo & XACT_COMPLETION_FORCE_SYNC_COMMIT) != 0)

typedef struct xl_xact_assignment
{
	TransactionId xtop;			/* 分配的XID的顶层XID */
	int			nsubxacts;		/* 子事务XID的数量 */
	TransactionId xsub[FLEXIBLE_ARRAY_MEMBER];	/* 分配的子XID */
} xl_xact_assignment;

#define MinSizeOfXactAssignment offsetof(xl_xact_assignment, xsub)

/*
 * 提交和中止记录可以包含大量信息。但很大
 * 一部分记录不需要所有可能的信息。因此我们
 * 只包含所需的信息。
 *
 * 最小的提交/中止记录仅由xl_xact_commit/abort
 * 结构组成。额外信息的存在通过在
 * 'xl_xact_xinfo->xinfo'中设置的位表示。xinfo字段
 * 本身的存在通过在xl_info字段中设置的XLOG_XACT_HAS_INFO位表示。
 *
 * 注意: 所有单个数据块的大小应该是
 * sizeof(int)的倍数，并且只需要int32对齐。如果它们需要更大的
 * 对齐，则在读取时需要被复制。
 */

/* 提交/中止的子记录 */

typedef struct xl_xact_xinfo
{
	/*
	 * 尽管现在我们只需要xinfo中的两个字节空间，
	 * 但我们使用四个字节，以便后续记录不必
	 * 关心对齐。提交记录可能很大，因此复制大块内容并不可取。
	 */
	uint32		xinfo;
} xl_xact_xinfo;

typedef struct xl_xact_dbinfo
{
	Oid			dbId;			/* MyDatabaseId */
	Oid			tsId;			/* MyDatabaseTableSpace */
} xl_xact_dbinfo;

typedef struct xl_xact_subxacts
{
	int			nsubxacts;		/* 子事务XID的数量 */
	TransactionId subxacts[FLEXIBLE_ARRAY_MEMBER];
} xl_xact_subxacts;
#define MinSizeOfXactSubxacts offsetof(xl_xact_subxacts, subxacts)

typedef struct xl_xact_relfilenodes
{
	int			nrels;			/* 关系的数量 */
	RelFileNode xnodes[FLEXIBLE_ARRAY_MEMBER];
} xl_xact_relfilenodes;
#define MinSizeOfXactRelfilenodes offsetof(xl_xact_relfilenodes, xnodes)

/*
 * 一个事务上丢弃的统计条目。
 *
 * 在这里声明而不是pgstat.h，因为pgstat.h不能从
 * 前端代码中包含，但WAL格式需要可由前端
 * 程序读取。
 */
typedef struct xl_xact_stats_item
{
	int			kind;
	Oid			dboid;
	Oid			objoid;
} xl_xact_stats_item;

typedef struct xl_xact_stats_items
{
	int			nitems;
	xl_xact_stats_item items[FLEXIBLE_ARRAY_MEMBER];
} xl_xact_stats_items;
#define MinSizeOfXactStatsItems offsetof(xl_xact_stats_items, items)

typedef struct xl_xact_invals
{
	int			nmsgs;			/* 共享无效消息的数量 */
	SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
} xl_xact_invals;
#define MinSizeOfXactInvals offsetof(xl_xact_invals, msgs)

typedef struct xl_xact_twophase
{
	TransactionId xid;
} xl_xact_twophase;

typedef struct xl_xact_origin
{
	XLogRecPtr	origin_lsn;
	TimestampTz origin_timestamp;
} xl_xact_origin;

typedef struct xl_xact_commit
{
	TimestampTz xact_time;		/* 提交时间 */

	/* 如果XLOG_XACT_HAS_INFO，则跟随xl_xact_xinfo */
	/* 如果XINFO_HAS_DBINFO，则跟随xl_xact_dbinfo */
	/* 如果XINFO_HAS_SUBXACT，则跟随xl_xact_subxacts */
	/* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */
	/* xl_xact_stats_items follows if XINFO_HAS_DROPPED_STATS */
	/* xl_xact_invals follows if XINFO_HAS_INVALS */
	/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
	/* twophase_gid follows if XINFO_HAS_GID. 作为一个以空字符终止的字符串。 */
	/* xl_xact_origin follows if XINFO_HAS_ORIGIN, 存储为未对齐！ */
} xl_xact_commit;
#define MinSizeOfXactCommit (offsetof(xl_xact_commit, xact_time) + sizeof(TimestampTz))

typedef struct xl_xact_abort
{
	TimestampTz xact_time;		/* 终止时间 */

	/* 如果XLOG_XACT_HAS_INFO，则跟随xl_xact_xinfo */
	/* 如果XINFO_HAS_DBINFO，则跟随xl_xact_dbinfo */
	/* 如果XINFO_HAS_SUBXACT，则跟随xl_xact_subxacts */
	/* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */
	/* xl_xact_stats_items follows if XINFO_HAS_DROPPED_STATS */
	/* 不需要无效化消息。 */
	/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
	/* twophase_gid follows if XINFO_HAS_GID. 作为一个以空字符终止的字符串。 */
	/* xl_xact_origin follows if XINFO_HAS_ORIGIN, 存储为未对齐！ */
} xl_xact_abort;
#define MinSizeOfXactAbort sizeof(xl_xact_abort)

typedef struct xl_xact_prepare
{
	uint32		magic;			/* 格式标识符 */
	uint32		total_len;		/* 实际文件长度 */
	TransactionId xid;			/* 原始事务 XID */
	Oid			database;		/* 它所在数据库的 OID */
	TimestampTz prepared_at;	/* 准备时间 */
	Oid			owner;			/* 运行事务的用户 */
	int32		nsubxacts;		/* 后续子事务 XID 的数量 */
	int32		ncommitrels;	/* 提交时删除的关系数量 */
	int32		nabortrels;		/* 中止时删除的关系数量 */
	int32		ncommitstats;	/* 提交时要丢弃的统计数据数量 */
	int32		nabortstats;	/* 放弃时要丢弃的统计数据数量 */
	int32		ninvalmsgs;		/* 缓存失效消息数量 */
	bool		initfileinval;	/* relcache 初始化文件需要失效吗？ */
	uint16		gidlen;			/* GID 的长度 - GID 紧跟在头部之后 */
	XLogRecPtr	origin_lsn;		/* 此记录在源节点的 lsn */
	TimestampTz origin_timestamp;	/* 在源节点的准备时间 */
} xl_xact_prepare;

/*
 * 上述格式的提交/放弃记录解析起来有点冗长，因此
 * 使用 ParseCommit/AbortRecord() 生成了解构版本，以便于处理。
 */
typedef struct xl_xact_parsed_commit
{
	TimestampTz xact_time;
	uint32		xinfo;

	Oid			dbId;			/* MyDatabaseId */
	Oid			tsId;			/* MyDatabaseTableSpace */

	int			nsubxacts;
	TransactionId *subxacts;

	int			nrels;
	RelFileNode *xnodes;

	int			nstats;
	xl_xact_stats_item *stats;

	int			nmsgs;
	SharedInvalidationMessage *msgs;

	TransactionId twophase_xid; /* 仅适用于 2PC */
	char		twophase_gid[GIDSIZE];	/* 仅适用于 2PC */
	int			nabortrels;		/* 仅适用于 2PC */
	RelFileNode *abortnodes;	/* 仅适用于 2PC */
	int			nabortstats;	/* 仅适用于 2PC */
	xl_xact_stats_item *abortstats; /* 仅适用于 2PC */

	XLogRecPtr	origin_lsn;
	TimestampTz origin_timestamp;
} xl_xact_parsed_commit;

typedef xl_xact_parsed_commit xl_xact_parsed_prepare;

typedef struct xl_xact_parsed_abort
{
	TimestampTz xact_time;
	uint32		xinfo;

	Oid			dbId;			/* MyDatabaseId */
	Oid			tsId;			/* MyDatabaseTableSpace */

	int			nsubxacts;
	TransactionId *subxacts;

	int			nrels;
	RelFileNode *xnodes;

	int			nstats;
	xl_xact_stats_item *stats;

	TransactionId twophase_xid; /* 仅适用于 2PC */
	char		twophase_gid[GIDSIZE];	/* 仅适用于 2PC */

	XLogRecPtr	origin_lsn;
	TimestampTz origin_timestamp;
} xl_xact_parsed_abort;


/* ----------------
 *		extern 定义
 * ----------------
 */
extern bool IsTransactionState(void);
extern bool IsAbortedTransactionBlockState(void);
#ifdef FDDQ
extern bool IsAbortedTransactionState(void);
#endif
extern TransactionId GetTopTransactionId(void);
extern TransactionId GetTopTransactionIdIfAny(void);
extern TransactionId GetCurrentTransactionId(void);
extern TransactionId GetCurrentTransactionIdIfAny(void);
extern TransactionId GetStableLatestTransactionId(void);
extern SubTransactionId GetCurrentSubTransactionId(void);
extern FullTransactionId GetTopFullTransactionId(void);
extern FullTransactionId GetTopFullTransactionIdIfAny(void);
extern FullTransactionId GetCurrentFullTransactionId(void);
extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
extern void MarkCurrentTransactionIdLoggedIfAny(void);
extern bool SubTransactionIsActive(SubTransactionId subxid);
extern CommandId GetCurrentCommandId(bool used);
extern void SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts);
extern TimestampTz GetCurrentTransactionStartTimestamp(void);
extern TimestampTz GetCurrentStatementStartTimestamp(void);
extern TimestampTz GetCurrentTransactionStopTimestamp(void);
extern void SetCurrentStatementStartTimestamp(void);
extern int	GetCurrentTransactionNestLevel(void);
extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
extern void CommandCounterIncrement(void);
extern void ForceSyncCommit(void);
extern void StartTransactionCommand(void);
extern void SaveTransactionCharacteristics(SavedTransactionCharacteristics *s);
extern void RestoreTransactionCharacteristics(const SavedTransactionCharacteristics *s);
extern void CommitTransactionCommand(void);
extern void AbortCurrentTransaction(void);
extern void BeginTransactionBlock(void);
extern bool EndTransactionBlock(bool chain);
extern bool PrepareTransactionBlock(const char *gid);
extern void UserAbortTransactionBlock(bool chain);
extern void BeginImplicitTransactionBlock(void);
extern void EndImplicitTransactionBlock(void);
extern void ReleaseSavepoint(const char *name);
extern void DefineSavepoint(const char *name);
extern void RollbackToSavepoint(const char *name);
extern void BeginInternalSubTransaction(const char *name);
extern void ReleaseCurrentSubTransaction(void);
extern void RollbackAndReleaseCurrentSubTransaction(void);
extern bool IsSubTransaction(void);
extern Size EstimateTransactionStateSpace(void);
extern void SerializeTransactionState(Size maxsize, char *start_address);
extern void StartParallelWorkerTransaction(char *tstatespace);
extern void EndParallelWorkerTransaction(void);
extern bool IsTransactionBlock(void);
extern bool IsTransactionOrTransactionBlock(void);
extern char TransactionBlockStatusCode(void);
extern void AbortOutOfAnyTransaction(void);
extern void PreventInTransactionBlock(bool isTopLevel, const char *stmtType);
extern void RequireTransactionBlock(bool isTopLevel, const char *stmtType);
extern void WarnNoTransactionBlock(bool isTopLevel, const char *stmtType);
extern bool IsInTransactionBlock(bool isTopLevel);
extern void RegisterXactCallback(XactCallback callback, void *arg);
extern void UnregisterXactCallback(XactCallback callback, void *arg);
extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
extern void UnregisterSubXactCallback(SubXactCallback callback, void *arg);

extern bool IsSubxactTopXidLogPending(void);
extern void MarkSubxactTopXidLogged(void);

extern int	xactGetCommittedChildren(TransactionId **ptr);

extern XLogRecPtr XactLogCommitRecord(TimestampTz commit_time,
									  int nsubxacts, TransactionId *subxacts,
									  int nrels, RelFileNode *rels,
									  int nstats,
									  xl_xact_stats_item *stats,
									  int nmsgs, SharedInvalidationMessage *msgs,
									  bool relcacheInval,
									  int xactflags,
									  TransactionId twophase_xid,
									  const char *twophase_gid);

extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
									 int nsubxacts, TransactionId *subxacts,
									 int nrels, RelFileNode *rels,
									 int nstats,
									 xl_xact_stats_item *stats,
									 int xactflags, TransactionId twophase_xid,
									 const char *twophase_gid);
extern void xact_redo(XLogReaderState *record);

/* xactdesc.c */
extern void xact_desc(StringInfo buf, XLogReaderState *record);
extern const char *xact_identify(uint8 info);

/* 也在 xactdesc.c 中，因此可以在前端/后端代码之间共享 */
extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
extern void ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *parsed);

extern void EnterParallelMode(void);
extern void ExitParallelMode(void);
extern bool IsInParallelMode(void);

#endif							/* XACT_H */
